/* * Copyright 2016 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package io.reactivex.netty.client.loadbalancer; import io.reactivex.netty.channel.Connection; import io.reactivex.netty.client.ConnectionProvider; import io.reactivex.netty.client.Host; import io.reactivex.netty.client.HostConnector; import io.reactivex.netty.client.events.ClientEventListener; import rx.Observable; import java.util.List; import java.util.concurrent.ThreadLocalRandom; public abstract class AbstractP2CStrategy<W, R, L extends ClientEventListener> implements LoadBalancingStrategy<W, R> { @Override public ConnectionProvider<W, R> newStrategy(final List<HostHolder<W, R>> hosts) { newHostsList(hosts.size()); return new ConnectionProvider<W, R>() { @Override public Observable<Connection<R, W>> newConnectionRequest() { HostHolder<W, R> selected = null; if (hosts.isEmpty()) { noUsableHostsFound(); return Observable.error(NoHostsAvailableException.EMPTY_INSTANCE); } else if (hosts.size() == 1) { HostHolder<W, R> holder = hosts.get(0); @SuppressWarnings("unchecked") L eventListener = (L) holder.getEventListener(); double weight = getWeight(eventListener); if (isUnusable(weight)) { noUsableHostsFound(); return Observable.error(new NoHostsAvailableException("No usable hosts found.")); } selected = holder; } else { ThreadLocalRandom rand = ThreadLocalRandom.current(); for (int i = 0; i < 5; i++) { int pos = rand.nextInt(hosts.size()); HostHolder<W, R> first = hosts.get(pos); int pos2 = (rand.nextInt(hosts.size() - 1) + pos + 1) % hosts.size(); HostHolder<W, R> second = hosts.get(pos2); @SuppressWarnings("unchecked") double w1 = getWeight((L) first.getEventListener()); @SuppressWarnings("unchecked") double w2 = getWeight((L) second.getEventListener()); if (w1 > w2) { selected = first; break; } else if (w1 < w2) { selected = second; break; } else if (!isUnusable(w1)) { selected = first; break; } foundTwoUnusableHosts(); } if (null == selected) { noUsableHostsFound(); return Observable.error(new NoHostsAvailableException("No usable hosts found after 5 tries.")); } } return selected.getConnector().getConnectionProvider().newConnectionRequest(); } }; } protected boolean isUnusable(double weight) { return weight < 0.0; } @Override public HostHolder<W, R> toHolder(HostConnector<W, R> connector) { return new HostHolder<>(connector, newListener(connector.getHost())); } protected abstract L newListener(Host host); protected abstract double getWeight(L listener); protected void noUsableHostsFound() { // No Op by default } protected void foundTwoUnusableHosts() { // No Op by default } protected void newHostsList(int size) { // No Op by default } }